#include <iostream>
#include "sin_synth.hpp"
#include "square_synth.hpp"
#include "sound_out.hpp"
#include "vibrato.hpp"
#include "arpeggio.hpp"
#include "sequencer.hpp"
#include "mixer.hpp"
#include "splitter.hpp"
#include "amplifier.hpp"
#include "const.hpp"
#include "limiter.hpp"

enum { GENERATION = 5 };

int fact(int n)
{
    int result = 1;
    for (int i = 1; i <= n; ++i)
        result *= i;
    return result;
}

class Durations: public DurationSequence
{
protected:
    void run()
    {
        setTempo(40);
        gen(2 * fact(GENERATION), 0);
        send(2);
    }
private:
    void gen(float n, int g)
    {
        if (g < GENERATION)
        {
            gen(n / 8 * 3, g + 1);
            gen(n / 4, g + 1);
            gen(n / 4, g + 1);
            gen(n / 8, g + 1);

            gen(n / 8 * 3, g + 1);
            gen(n / 4, g + 1);
            gen(n / 4, g + 1);
            gen(n / 8, g + 1);

            gen(n / 8 * 3, g + 1);
            gen(n / 4, g + 1);
            gen(n / 4, g + 1);
            gen(n / 8, g + 1);

            gen(n, g + 1);
        }
        else
            send(n);
    }
};

class Melody: public Sequence<float>
{
protected:
    void run()
    {
        gen(220, 0);
        send(220);
    }
private:
    void gen(float n, int g)
    {
        if (g < GENERATION)
        {
            gen(n, g + 1);
            gen(n / 2 * 3, g + 1);
            gen(n, g + 1);
            gen(n / 3 * 2, g + 1);
            gen(n, g + 1);
            gen(n / 5 * 4, g + 1);
            gen(n, g + 1);
            gen(n / 4 * 5, g + 1);
            gen(n, g + 1);
            gen(n / 5 * 3, g + 1);
            gen(n, g + 1);
            gen(n / 3 * 5, g + 1);
        }
        else
            send(n);
    }
};

int main()
{
    SoundOut<Amplifier<SquareSynth<Sequencer<Melody, Durations> >, DampedSequencer<FloatConst<1, 1>, Durations, FloatConst<99990, 100000> > > > out;
    while (out.tick()) ;
}
